#include "datetime.h"
#include <string.h>
#include <iostream>
#include <algorithm>
#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif
#define DATETIME_RESOLUTION_MICROSECOND 1000000

#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b))
#endif

#ifndef min
#define min(a,b)            (((a) < (b)) ? (a) : (b))
#endif


static void __local_time(int64_t microsec, struct tm *tm)
{
    int64_t sec = microsec / DATETIME_RESOLUTION_MICROSECOND;
#ifdef _WIN32
    localtime_s(tm, (std::time_t *)&sec);
#else
    localtime_r((std::time_t *)&sec, tm);
#endif
}

DateTime::DateTime()
{
    update();
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond)
{
    struct tm tmp;
    memset(&tmp, 0, sizeof(tmp));
    tmp.tm_year = year - 1900;
    tmp.tm_mon = month - 1;
    tmp.tm_mday = day;
    tmp.tm_hour = hour;
    tmp.tm_min = minute;
    tmp.tm_sec = second;
    std::time_t sec = std::mktime(&tmp);

    ts_ = sec * DATETIME_RESOLUTION_MICROSECOND + millisecond * 1000 + microsecond;
    __local_time(ts_, &tm_);
}
DateTime::~DateTime() {}
DateTime::DateTime(const DateTime &other)
{
    ts_ = other.ts_;
    tm_ = other.tm_;
}
DateTime &DateTime::operator=(const DateTime &other)
{
    ts_ = other.ts_;
    tm_ = other.tm_;
    return *this;
}

void DateTime::update()
{
    memset(&tm_, 0, sizeof(tm_));
    ts_ = currentMicrosecondsSinceEpoch();
    std::time_t t = ts_ / DATETIME_RESOLUTION_MICROSECOND;
    __local_time(ts_, &tm_);
}

int64_t DateTime::millisecondsSinceEpoch() const
{
    return ts_ / 1000;
}

int64_t DateTime::secondsSinceEpoch() const
{
    return ts_ / DATETIME_RESOLUTION_MICROSECOND;
}

static std::string DateTimeFormat_WEEKDAY_NAMES[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };

static std::string DateTimeFormat_MONTH_NAMES[]
    = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
int DateTime::year() const
{
    return tm_.tm_year + 1900;
}
/// Returns the year.

int DateTime::month() const
{
    return tm_.tm_mon + 1;
}
/// Returns the month (1 to 12).

int DateTime::week(int firstDayOfWeek) const
{
    /// find the first firstDayOfWeek.
    int baseDay = 1;
    while (DateTime(year(), 1, baseDay).dayOfWeek() != firstDayOfWeek)
        ++baseDay;

    int doy = dayOfYear();
    int offs = baseDay <= 4 ? 0 : 1;
    if (doy < baseDay)
        return offs;
    else
        return (doy - baseDay) / 7 + 1 + offs;
}

int DateTime::day() const
{
    return tm_.tm_mday;
}

int DateTime::dayOfWeek() const
{
    return tm_.tm_wday;
}

int DateTime::dayOfYear() const
{
    return tm_.tm_yday + 1;
}

int DateTime::hour() const
{
    return tm_.tm_hour;
}

int DateTime::hourAMPM() const
{
    if (tm_.tm_hour < 1)
        return 12;
    else if (tm_.tm_hour > 12)
        return tm_.tm_hour - 12;
    else
        return tm_.tm_hour;
}

bool DateTime::isAM() const
{
    return tm_.tm_hour < 12;
}

bool DateTime::isPM() const
{
    return tm_.tm_hour >= 12;
}

int DateTime::minute() const
{
    return tm_.tm_min;
}

int DateTime::second() const
{
    return tm_.tm_sec;
}

int DateTime::millisecond() const
{
    if (ts_ == 0)
        return 0;
    return (ts_ / 1000) % (ts_ / 1000000);
}

int DateTime::microsecond() const
{
    if (ts_ == 0)
        return 0;
    return ts_ % (ts_ / 1000);
}
static const unsigned NF_MAX_INT_STRING_LEN = 32; // increase for 64-bit binary formatting support
static const unsigned NF_MAX_FLT_STRING_LEN = 780;

void NumberFormatter_append(std::string &str, int value, int width)
{
    static std::string INT_FORMAT[] = { "%d", "%d", "%02d", "%03d", "%04d", "%05d", "%06d", "%07d", "%08d", "%09d", "%10d" };

    char format[32] = { 0 };
    char result[NF_MAX_INT_STRING_LEN];
    if (width < 0)
        sprintf(result, "%d", value);
    else
        sprintf(result, INT_FORMAT[width].c_str(), value);

    str.append(result, strlen(result));
}

static void NumberFormatter_append0(std::string &str, int value, int width)
{
    static std::string INT_FORMAT[] = { "%d", "%d", "%02d", "%03d", "%04d", "%05d", "%06d", "%07d", "%08d", "%09d", "%10d" };

    char format[32] = { 0 };
    char result[NF_MAX_INT_STRING_LEN];
    if (width < 0)
        sprintf(result, "%d", value);
    else
        sprintf(result, INT_FORMAT[width].c_str(), value);

    str.append(result, strlen(result));
}

static int repeatCount(const char *str)
{

    if (str == 0 || *str == 0)
        return 0;
    std::string s = str;
    const char c = s.front();
    int j = 1;
    while (j < s.size() && s.at(j) == c)
        ++j;
    return int(j);
}
static bool nextCharIs(const char *str, int pos, char c)
{
    if (pos + 1 >= strlen(str))
        return false;
    return str[pos + 1] == c;
}
std::string DateTime::toString(const std::string &format) const
{
    bool formatDate = true;
    bool formatTime = true;
    std::string result;
    int i = 0;
    while (i < format.length())
    {

        const char c = format.at(i);
        int repeat = repeatCount(format.data() + i);
        bool used = false;
        if (formatDate)
        {
            switch (c)
            {
                case 'y':
                    used = true;
                    if (repeat >= 4)
                        repeat = 4;
                    else if (repeat >= 2)
                        repeat = 2;

                    switch (repeat)
                    {
                        case 4:
                        {
                            const int len = (year() < 0) ? 5 : 4;
                            NumberFormatter_append(result, year(), 4);
                            break;
                        }
                        case 2: NumberFormatter_append(result, year() % 100, 2); break;
                        default:
                            repeat = 1;
                            result.append(1, c);
                            break;
                    }
                    break;

                case 'M':
                    used = true;
                    repeat = min(repeat, 4);
                    switch (repeat)
                    {
                        case 1: NumberFormatter_append(result, month(), 0); break;
                        case 2: NumberFormatter_append(result, month(), 2); break;
                    }
                    break;

                case 'd':
                    used = true;
                    repeat = min(repeat, 4);
                    switch (repeat)
                    {
                        case 1: NumberFormatter_append(result, day(), 0); break;
                        case 2: NumberFormatter_append(result, day(), 2); break;
                    }
                    break;

                default: break;
            }
        }
        if (!used && formatTime)
        {
            switch (c)
            {
                case 'h':
                {
                    used = true;
                    repeat = min(repeat, 2);
                    int hour = this->hour();
                    if (0 != strchr(format.c_str(), 'a') || 0 != strchr(format.c_str(), 'A'))
                    {
                        hour = this->hourAMPM();
                    }

                    switch (repeat)
                    {
                        case 1: NumberFormatter_append(result, hour, 1); break;
                        case 2: NumberFormatter_append(result, hour, 2); break;
                    }
                    break;
                }
                case 'H':
                    used = true;
                    repeat = min(repeat, 2);
                    switch (repeat)
                    {
                        case 1: NumberFormatter_append(result, hour(), 1); break;
                        case 2: NumberFormatter_append(result, hour(), 2); break;
                    }
                    break;

                case 'm':
                    used = true;
                    repeat = min(repeat, 2);
                    switch (repeat)
                    {
                        case 1: NumberFormatter_append(result, minute(), 1); break;
                        case 2: NumberFormatter_append(result, minute(), 2); break;
                    }
                    break;

                case 's':
                    used = true;
                    repeat = min(repeat, 2);
                    switch (repeat)
                    {
                        case 1: NumberFormatter_append(result, second(), 1); break;
                        case 2: NumberFormatter_append(result, second(), 2); break;
                    }
                    break;

                case 'a':
                    used = true;

                    repeat = nextCharIs(format.c_str(), i, 'p') ? 2 : 1;
                    result.append(isAM() ? "am" : "pm");
                    break;

                case 'A':
                    used = true;
                    repeat = nextCharIs(format.c_str(), i, 'P') ? 2 : 1;
                    result.append(isAM() ? "AM" : "PM");
                    break;

                case 'z':
                    used = true;
                    repeat = (repeat >= 3) ? 3 : 1;

                    // note: the millisecond component is treated like the decimal part of the seconds
                    // so ms == 2 is always printed as "002", but ms == 200 can be either "2" or "200"
                    if (repeat == 1)
                        NumberFormatter_append(result, millisecond() / 100, 3);
                    else if (repeat == 2)
                        NumberFormatter_append(result, millisecond() / 10, 3);
                    else
                        NumberFormatter_append(result, millisecond(), 3);

                    break;

                case 't':
                    used = true;
                    repeat = 1;
                    // If we have a QDateTime use the time spec otherwise use the current system tzname
                    // TODO
                    break;

                default: break;
            }
        }
        if (!used)
            result.append(std::string(repeat, c));
        i += repeat;
    }

    return result;
}
/*
std::string DateTime::toString2(const std::string& format) const
{
    std::string str;
    std::string::const_iterator it = format.begin();
    std::string::const_iterator end = format.end();
    while (it != end)
    {
        if (*it == '%')
        {
            if (++it != end)
            {
                switch (*it)
                {
                case 'w': str.append(DateTimeFormat_WEEKDAY_NAMES[dayOfWeek()], 0, 3); break;
                case 'W': str.append(DateTimeFormat_WEEKDAY_NAMES[dayOfWeek()]); break;
                case 'b': str.append(DateTimeFormat_MONTH_NAMES[month() - 1], 0, 3); break;
                case 'B': str.append(DateTimeFormat_MONTH_NAMES[month() - 1]); break;
                case 'd': NumberFormatter_append0(str, day(), 2); break;
                case 'e': NumberFormatter_append(str, day(),-1); break;
                case 'f': NumberFormatter_append(str, day(), 2); break;
                case 'm': NumberFormatter_append0(str, month() + 1, 2); break;
                case 'n': NumberFormatter_append(str, month() + 1, -1); break;
                case 'o': NumberFormatter_append(str, month() + 1, 2); break;
                case 'y': NumberFormatter_append0(str, year() % 100, 2); break;
                case 'Y': NumberFormatter_append0(str, year(), 4); break;
                case 'H': NumberFormatter_append0(str, hour(), 2); break;
                case 'h': NumberFormatter_append0(str, hourAMPM(), 2); break;
                case 'a': str.append(isAM() ? "am" : "pm"); break;
                case 'A': str.append(isAM() ? "AM" : "PM"); break;
                case 'M': NumberFormatter_append0(str, minute(), 2); break;
                case 'S': NumberFormatter_append0(str, second(), 2); break;
                case 's':
                    NumberFormatter_append0(str, second(), 2);
                    str += '.';
                    NumberFormatter_append0(str, millisecond() * 1000 + microsecond(), 6);
                    break;
                case 'i': NumberFormatter_append0(str, millisecond(), 3); break;
                case 'c': NumberFormatter_append(str, millisecond() / 100, -1); break;
                case 'F': NumberFormatter_append0(str, millisecond() * 1000 + microsecond(), 6); break;
                default: str += *it;
                }
                ++it;
            }
        }
        else
            str += *it++;
    }

    return str;
}
*/
int64_t DateTime::currentSecondsSinceEpoch()
{
    return currentMicrosecondsSinceEpoch() / DATETIME_RESOLUTION_MICROSECOND;
}

int64_t DateTime::currentMillisecondsSince()
{
    return currentMicrosecondsSinceEpoch() / 1000;
}

int64_t DateTime::currentMicrosecondsSinceEpoch()
{

#if defined(_WIN32)

    FILETIME ft;
    GetSystemTimeAsFileTime(&ft);

    ULARGE_INTEGER epoch; // UNIX epoch (1970-01-01 00:00:00) expressed in Windows NT FILETIME
    epoch.LowPart = 0xD53E8000;
    epoch.HighPart = 0x019DB1DE;

    ULARGE_INTEGER ts;
    ts.LowPart = ft.dwLowDateTime;
    ts.HighPart = ft.dwHighDateTime;
    ts.QuadPart -= epoch.QuadPart;
    return ts.QuadPart / 10;

#else

    struct timeval tv;
    if (gettimeofday(&tv, NULL))
        return 0;
    return (tv.tv_sec * DATETIME_RESOLUTION_MICROSECOND + tv.tv_usec);

#endif
}
DateTime DateTime::fromMilliSecondsSinceEpoch(int64_t msecs)
{
    DateTime dt;
    dt.ts_ = msecs * 1000; // Microseconds since epoch
    std::time_t t = msecs / 1000;
    __local_time(dt.ts_, &dt.tm_);
    return dt;
}
DateTime DateTime::fromSecondsSinceEpoch(int64_t secs)
{
    DateTime dt;
    dt.ts_ = secs * DATETIME_RESOLUTION_MICROSECOND; // Microseconds since epoch
    __local_time(dt.ts_, &dt.tm_);
    return dt;
}

static DateTime fromString(const std::string& dateTimeStr) {
    int year, month, day, hour, minute, second;
    
    // 使用 sscanf 解析 "yyyy-MM-dd hh:mm:ss" 格式
    int result = sscanf(dateTimeStr.c_str(), "%d-%d-%d %d:%d:%d", 
                        &year, &month, &day, &hour, &minute, &second);
    
    // 检查是否成功解析了所有6个字段
    if (result != 6) {
        // 如果解析失败，返回当前时间（或抛出异常）
        return DateTime();
    }
    
    return DateTime(year, month, day, hour, minute, second);
}

// 计算两个时间字符串之间的差值并返回 hh:mm:ss 格式
std::string calculateDuration(const std::string& startTimeStr, const std::string& endTimeStr) {
    // 解析时间字符串为 DateTime 对象
    DateTime startDateTime = fromString(startTimeStr);
    DateTime endDateTime = fromString(endTimeStr);
    
    // 计算时间差（秒）
    int64_t startSeconds = startDateTime.secondsSinceEpoch();
    int64_t endSeconds = endDateTime.secondsSinceEpoch();
    int64_t durationSeconds = endSeconds - startSeconds;
    
    // 处理负值情况
    if (durationSeconds < 0) {
        durationSeconds = -durationSeconds;
    }
    
    // 转换为小时、分钟、秒
    int hours = durationSeconds / 3600;
    int minutes = (durationSeconds % 3600) / 60;
    int seconds = durationSeconds % 60;
    
    // 格式化为 hh:mm:ss 字符串
    std::ostringstream oss;
    oss << std::setfill('0') << std::setw(2) << hours << ":"
        << std::setfill('0') << std::setw(2) << minutes << ":"
        << std::setfill('0') << std::setw(2) << seconds;
    
    return oss.str();
}
int main()
{
    std::cout<< DateTime().toString() << std::endl;
    std::string startTimeStr = "2023-04-05 10:30:00";
    std::string endTimeStr = "2023-04-05 12:45:00";
    std::cout << calculateDuration(startTimeStr, endTimeStr) << std::endl;
}